<div class="content-section introduction">
    <div class="feature-intro">
        <h1>DataView</h1>
        <p>DataView displays data in grid or list layout with pagination and sorting features.</p>
    </div>
    <app-demoActions github="dataview" stackblitz="dataview-demo"></app-demoActions>
</div>

<div class="content-section implementation">
    <div class="card">
        <p-dataView #dv [value]="products" [paginator]="true" [rows]="9" filterBy="name"
            [sortField]="sortField" [sortOrder]="sortOrder" layout="grid">
            <ng-template pTemplate="header">
                <div class="flex flex-column md:flex-row md:justify-content-between">
                    <p-dropdown [options]="sortOptions" [(ngModel)]="sortKey" placeholder="Sort By Price" (onChange)="onSortChange($event)" styleClass="mb-2 md:mb-0"></p-dropdown>
                    <span class="p-input-icon-left mb-2 md:mb-0">
                        <i class="pi pi-search"></i>
                        <input type="search" pInputText placeholder="Search by Name" (input)="dv.filter($event.target.value)">
                    </span>
                    <p-dataViewLayoutOptions></p-dataViewLayoutOptions>
                </div>
            </ng-template>
            <ng-template let-product pTemplate="listItem">
                <div class="col-12">
                    <div class="product-list-item">
                        <img [src]="'assets/showcase/images/demo/product/' + product.image" [alt]="product.name"/>
                        <div class="product-list-detail">
                            <div class="product-name">{{product.name}}</div>
                            <div class="product-description">{{product.description}}</div>
                            <p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating>
                            <i class="pi pi-tag product-category-icon"></i><span class="product-category">{{product.category}}</span>
                        </div>
                        <div class="product-list-action">
                            <span class="product-price">${{product.price}}</span>
                            <p-button icon="pi pi-shopping-cart" label="Add to Cart" [disabled]="product.inventoryStatus === 'OUTOFSTOCK'"></p-button>
                            <span [class]="'product-badge status-' + product.inventoryStatus.toLowerCase()">{{product.inventoryStatus}}</span>
                        </div>
                    </div>
                </div>
            </ng-template>
            <ng-template let-product pTemplate="gridItem">
                <div class="col-12 md:col-4">
                    <div class="product-grid-item card">
                        <div class="product-grid-item-top">
                            <div>
                                <i class="pi pi-tag product-category-icon"></i>
                                <span class="product-category">{{product.category}}</span>
                            </div>
                            <span [class]="'product-badge status-' + product.inventoryStatus.toLowerCase()">{{product.inventoryStatus}}</span>
                        </div>
                        <div class="product-grid-item-content">
                            <img [src]="'assets/showcase/images/demo/product/' + product.image" [alt]="product.name"/>
                            <div class="product-name">{{product.name}}</div>
                            <div class="product-description">{{product.description}}</div>
                            <p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"></p-rating>
                        </div>
                        <div class="product-grid-item-bottom">
                            <span class="product-price">${{product.price}}</span>
                            <p-button icon="pi pi-shopping-cart" [disabled]="product.inventoryStatus === 'OUTOFSTOCK'"></p-button>
                        </div>
                    </div>
                </div>
            </ng-template>
        </p-dataView>
    </div>
</div>

<div class="content-section documentation">
    <p-tabView>
        <p-tabPanel header="Documentation">
            <h5>Import</h5>
<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
import &#123;DataViewModule&#125; from 'primeng/dataview';
</app-code>

            <h5>PrimeFlex</h5>
            <p>DataView utilizes PrimeFlex so it is required to be installed in your application. Refer to the <a [routerLink]="['/flexgrid']">documentation</a> for details.</p>

            <h5>Getting Started</h5>
            <p>DataView requires a collection of items as its value and one or more templates depending on the layout mode e.g. <i>list</i> and <i>grid</i>.</p>

            <p>Throughout the samples, a car interface having vin, brand, year and color properties are used to define an object to be displayed by the dataview. Cars are loaded by a CarService that connects to a server to fetch the cars.</p>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export interface Car &#123;
    vin;
    year;
    brand;
    color;
&#125;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
import &#123;Injectable&#125; from 'angular2/core';
import &#123;Http, Response&#125; from 'angular2/http';
import &#123;Car&#125; from '../domain/car';

@Injectable()
export class CarService &#123;

    constructor(private http: Http) &#123;&#125;

    getCarsLarge() &#123;
        return this.http.get('/showcase/resources/data/cars-large.json')
                    .toPromise()
                    .then(res => &lt;Car[]&gt; res.json().data)
                    .then(data => &#123; return data; &#125;);
    &#125;
&#125;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export class DataViewDemo implements OnInit &#123;

    cars: Car[];

    constructor(private carService: CarService) &#123; &#125;

    ngOnInit() &#123;
        this.carService.getCarsLarge().then(cars => this.cars = cars);
    &#125;
&#125;
</app-code>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars"&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

            <h5>Layouts</h5>
            <p>DataView has two layout modes; "list" and "grid" where a separate template is used to render an item in each mode. In list mode name of the template is "listItem" whereas
                in grid mode it is "gridItem". In grid mode, the ng-template element should contain a div element as a wrapper with <a href="https://www.primefaces.org/primeflex/gridsystem" target="_blank">PrimeFlex</a> style class of your choice.</p>
            <p>Note that there is no restriction to use both layouts at the same time, you may configure only one layout using the layout property with the corresponding ng-template.</p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars"&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &lt;div class="col-12 md:col-3"&gt;
            &#123;&#123;car.year&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

            <h5>Sections</h5>
            <p>Header and Footers sections can be customized using <i>header</i> and <i>footer</i> templates.</p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars"&gt;
    &lt;ng-template pTemplate="header"&gt;List of Cars&lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &lt;div class="col-12 md:col-3"&gt;
            &#123;&#123;car.year&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template pTemplate="footer"&gt;Choose from the list.&lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

            <h5>DataViewLayoutOptions</h5>
            <p>When both layout modes are enabled in DataView, a UI element would be necessary to let the user toggle between the view. <i>p-dataViewLayoutOptions</i> is a helper component
                to display a buttonset to choose the layout mode in DataView. Location of the <i>p-dataViewLayoutOptions</i> should be inside the DataView component. If you prefer a different UI element
                you can create your own that updates the layout property of the DataView.
            </p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars"&gt;
    &lt;ng-template pTemplate="header"&gt;
        &lt;p-dataViewLayoutOptions&gt;&lt;/p-dataViewLayoutOptions&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &lt;div class="col-12 md:col-3"&gt;
            &#123;&#123;car.year&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template pTemplate="footer"&gt;
        &lt;p-dataViewLayoutOptions&gt;&lt;/p-dataViewLayoutOptions&gt;
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

            <h5>Paginator</h5>
            <p>Pagination is enabled by setting paginator property to true, rows attribute defines the number of rows per page and pageLinks specify the the number
                of page links to display. To customize the paginator, use <i>paginatorLeftTemplate</i>, <i>paginatorRightTemplate</i> and <i>paginatorDropdownItemTemplate</i> templates.</p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars" [paginator]="true" [rows]="10"&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &lt;div class="col-12 md:col-3"&gt;
            &#123;&#123;car.year&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template pTemplate="paginatorleft" let-state&gt;
        &#123;&#123;state.first&#125;&#125;
        &lt;button type="button" pButton icon="pi-refresh"&gt;&lt;/button&gt;
    &lt;/ng-template&gt;
    &lt;ng-template pTemplate="paginatorright"&gt;
        &lt;button type="button" pButton icon="pi-cloud-upload"&gt;&lt;/button&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-item pTemplate="paginatordropdownitem"&gt;
        &#123;&#123;item.value&#125;&#125; - per page
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

            <h5>Lazy Loading</h5>
            <p>Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded by invoking
             onLazyLoad callback everytime paging happens. To implement lazy loading, enable lazy attribute and provide a method callback using <i>onLazyLoad</i> that actually loads the data from a remote datasource. <i>onLazyLoad</i> gets an event object
            that contains information about what to load. It is also important to assign the logical number of rows to totalRecords by doing a projection query for paginator configuration so that paginator
            displays the UI assuming there are actually records of totalRecords size although in reality they aren't as in lazy mode, only the records that are displayed on the current page exist.</p>


<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars" [paginator]="true" [rows]="10"
    [lazy]="true" (onLazyLoad)="loadData($event)" [totalRecords]="totalRecords"&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &lt;div class="col-12 md:col-3"&gt;
            &#123;&#123;car.year&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
loadData(event) &#123;
    //event.first = First row offset
    //event.rows = Number of rows per page
&#125;
</app-code>

            <h5>Sorting</h5>
            <p><i>sortField</i> and <i>sortOrder</i> properties are available for sorting functionality, for flexibility there is no built-in UI available so that a custom UI can be used for the sorting element.
                Here is an example that uses a dropdown where simply updating the <i>sortField</i> - <i>sortOrder</i> bindings of the DataView initiates sorting.</p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView [value]="cars" [sortField]="sortField" [sortOrder]="sortOrder"&gt;
    &lt;ng-template pTemplate="header"&gt;
        &lt;p-dropdown [options]="sortOptions" [(ngModel)]="sortKey" placeholder="Sort By"
            (onChange)="onSortChange($event)" [style]="&#123;'min-width':'15em'&#125;"&gt;&lt;/p-dropdown&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &lt;div&gt;
            &#123;&#123;car.id&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &lt;div class="col-12 md:col-3"&gt;
            &#123;&#123;car.year&#125;&#125;
        &lt;/div&gt;
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export class DataViewSortDemo implements OnInit &#123;

    cars: Car[];

    sortOptions: SelectItem[];

    sortKey: string;

    sortField: string;

    sortOrder: number;

    constructor(private carService: CarService) &#123; &#125;

    ngOnInit() &#123;
        this.carService.getCarsLarge().then(cars => this.cars = cars);

        this.sortOptions = [
            &#123;label: 'Newest First', value: '!year'&#125;,
            &#123;label: 'Oldest First', value: 'year'&#125;,
            &#123;label: 'Brand', value: 'brand'&#125;
        ];
    &#125;

    onSortChange(event) &#123;
        let value = event.value;

        if (value.indexOf('!') === 0) &#123;
            this.sortOrder = -1;
            this.sortField = value.substring(1, value.length);
        &#125;
        else &#123;
            this.sortOrder = 1;
            this.sortField = value;
        &#125;
    &#125;
&#125;
</app-code>

            <h5>Filtering</h5>
            <p>Filtering is implemented by defining the filterBy property, match mode parameters and calling the filter function of the component,
                for flexibility there is no built-in UI available so that a custom UI can be used for the filter element.
                Available match modes are "contains"(Default), "startsWith", "endsWith", "equals", "notEquals", "in", "lt", "lte", "gt" and "gte".
                Here is an example that uses an input field. filterBy is a string and multiple fields can be defined with a comma separated list.</p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataView #dv [value]="cars" filterBy="brand"&gt;
    &lt;ng-template pTemplate="header"&gt;
        &lt;input type="search" pInputText placeholder="Search by brand" (input)="dv.filter($event.target.value, "contains")"&gt;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="listItem"&gt;
        &#123;&#123;car.id&#125;&#125;
    &lt;/ng-template&gt;
    &lt;ng-template let-car pTemplate="gridItem"&gt;
        &#123;&#123;car.year&#125;&#125;
    &lt;/ng-template&gt;
&lt;/p-dataView&gt;
</app-code>

            <h5>Custom UI Elements</h5>
            <p>As mentioned above, layout options selector, sorting and filtering are baked-in and no strict UI is enforces to make it possible to come up with your own UI elements
                to enable these features.
            </p>

            <h5>Loading Status</h5>
            <p>DataView has a loading property, when enabled a spinner icon is displayed to indicate data load.
                An optional loadingIcon property can be passed in case you'd like a different loading icon.</p>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;p-dataview [value]="cars" [loading]="loading"&gt;
    //content
&lt;/p-dataview&gt;
</app-code>

            <h5>Properties</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Type</th>
                            <th>Default</th>
                            <th>Description</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>value</td>
                            <td>array</td>
                            <td>null</td>
                            <td>An array of objects to display.</td>
                        </tr>
                        <tr>
                            <td>layout</td>
                            <td>string</td>
                            <td>list</td>
                            <td>Layout of the items, valid values are "list" and "grid".</td>
                        </tr>
                        <tr>
                            <td>paginator</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>When specified as true, enables the pagination.</td>
                        </tr>
                        <tr>
                            <td>rows</td>
                            <td>number</td>
                            <td>null</td>
                            <td>Number of rows to display per page.</td>
                        </tr>
                        <tr>
                            <td>totalRecords</td>
                            <td>number</td>
                            <td>null</td>
                            <td>Number of total records, defaults to length of value when not defined.</td>
                        </tr>
                        <tr>
                            <td>pageLinks</td>
                            <td>number</td>
                            <td>5</td>
                            <td>Number of page links to display in paginator.</td>
                        </tr>
                        <tr>
                            <td>rowsPerPageOptions</td>
                            <td>array</td>
                            <td>null</td>
                            <td>Array of integer/object values to display inside rows per page dropdown of paginator</td>
                        </tr>
                        <tr>
                            <td>paginatorPosition</td>
                            <td>string</td>
                            <td>bottom</td>
                            <td>Position of the paginator, options are "top","bottom" or "both".</td>
                        </tr>
                        <tr>
                            <td>alwaysShowPaginator</td>
                            <td>boolean</td>
                            <td>true</td>
                            <td>Whether to show it even there is only one page.</td>
                        </tr>
                        <tr>
                            <td>showFirstLastIcon</td>
                            <td>boolean</td>
                            <td>true</td>
                            <td>When enabled, icons are displayed on paginator to go first and last page.</td>
                        </tr>
                        <tr>
                            <td>paginatorDropdownAppendTo</td>
                            <td>any</td>
                            <td>null</td>
                            <td>Target element to attach the paginator dropdown overlay, valid values are "body" or a local ng-template variable of another element (note: use binding with brackets for template variables, e.g. [appendTo]="mydiv" for a div element having #mydiv as variable name).</td>
                        </tr>
                        <tr>
                            <td>paginatorDropdownScrollHeight</td>
                            <td>string</td>
                            <td>200px</td>
                            <td>Paginator dropdown height of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.</td>
                        </tr>
                        <tr>
                            <td>currentPageReportTemplate</td>
                            <td>string</td>
                            <td>(&#123;currentPage&#125; of &#123;totalPages&#125;)</td>
                            <td>Template of the current page report element. Available placeholders are
                                &#123;currentPage&#125;,&#123;totalPages&#125;,&#123;rows&#125;,&#123;first&#125;,&#123;last&#125; and &#123;totalRecords&#125;
                            </td>
                        </tr>
                        <tr>
                            <td>showCurrentPageReport</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Whether to display current page report.</td>
                        </tr>
                        <tr>
                            <td>showJumpToPageDropdown</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Whether to display a dropdown to navigate to any page.</td>
                        </tr>
                        <tr>
                            <td>showPageLinks</td>
                            <td>boolean</td>
                            <td>true</td>
                            <td>Whether to show page links.</td>
                        </tr>
                        <tr>
                            <td>lazy</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Defines if data is loaded and interacted with in lazy manner.</td>
                        </tr>
                        <tr>
                            <td>emptyMessage</td>
                            <td>string</td>
                            <td>No records found.</td>
                            <td>Text to display when there is no data. Defaults to global value in i18n translation configuration.</td>
                        </tr>
                        <tr>
                            <td>style</td>
                            <td>string</td>
                            <td>null</td>
                            <td>Inline style of the component.</td>
                        </tr>
                        <tr>
                            <td>styleClass</td>
                            <td>string</td>
                            <td>null</td>
                            <td>Style class of the component.</td>
                        </tr>
                        <tr>
                            <td>trackBy</td>
                            <td>Function</td>
                            <td>null</td>
                            <td>Function to optimize the dom operations by delegating to ngForTrackBy, default algoritm checks for object identity.</td>
                        </tr>
                        <tr>
                            <td>filterBy</td>
                            <td>string</td>
                            <td>null</td>
                            <td>Comma separated list of fields in the object graph to search against.</td>
                        </tr>
                        <tr>
                            <td>filterLocale</td>
                            <td>string</td>
                            <td>undefined</td>
                            <td>Locale to use in filtering. The default locale is the host environment's current locale.</td>
                        </tr>
                        <tr>
                            <td>loading</td>
                            <td>boolean</td>
                            <td>false</td>
                            <td>Displays a loader to indicate data load is in progress.</td>
                        </tr>
                        <tr>
                            <td>loadingIcon</td>
                            <td>string</td>
                            <td>pi pi-spinner</td>
                            <td>The icon to show while indicating data load is in progress.</td>
                        </tr>
                        <tr>
                            <td>first</td>
                            <td>number</td>
                            <td>0</td>
                            <td>Index of the first row to be displayed.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Events</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                    <tr>
                        <th>Name</th>
                        <th>Parameters</th>
                        <th>Description</th>
                    </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>onLazyLoad</td>
                            <td>event.first = First row offset <br>
                                event.rows = Number of rows per page <br></td>
                            <td>Callback to invoke when paging, sorting or filtering happens in lazy mode.</td>
                        </tr>
                        <tr>
                            <td>onPage</td>
                            <td>event.first: Index of first record in page<br>
                                event.rows: Number of rows on the page</td>
                            <td>Callback to invoke when pagination occurs.</td>
                        </tr>
                        <tr>
                            <td>onSort</td>
                            <td>event.sortField: Name of the sort field.<br>
                                event.sortOrder: Order of the sort.</td>
                            <td>Callback to invoke when sorting occurs.</td>
                        </tr>
                        <tr>
                            <td>onChangeLayout</td>
                            <td>event.layout: New layout</td>
                            <td>Callback to invoke when changing layout.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Templates</h5>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Parameters</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>header</td>
                            <td>-</td>
                        </tr>
                        <tr>
                            <td>paginatorLeft</td>
                            <td>-</td>
                        </tr>
                        <tr>
                            <td>paginatorRight</td>
                            <td>-</td>
                        </tr>
                        <tr>
                            <td>gridItem</td>
                            <td>$implicit: Data of the grid <br />
                                index: Index of the grid</td>
                        </tr>
                        <tr>
                            <td>listItemTemplate</td>
                            <td>$implicit: Data of the row <br />
                                index: Index of the row</td>
                        </tr>
                        <tr>
                            <td>empty</td>
                            <td>-</td>
                        </tr>
                        <tr>
                            <td>footer</td>
                            <td>-</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Styling</h5>
            <p>Following is the list of structural style classes, for theming classes visit <a href="#" [routerLink]="['/theming']">theming page</a>.</p>
            <div class="doc-tablewrapper">
                <table class="doc-table">
                    <thead>
                    <tr>
                        <th>Name</th>
                        <th>Element</th>
                    </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>p-dataview</td>
                            <td>Container element.</td>
                        </tr>
                        <tr>
                            <td>p-dataview-list</td>
                            <td>Container element in list layout.</td>
                        </tr>
                        <tr>
                            <td>p-dataview-grid</td>
                            <td>Container element in grid layout.</td>
                        </tr>
                        <tr>
                            <td>p-dataview-header</td>
                            <td>Header section.</td>
                        </tr>
                        <tr>
                            <td>p-dataview-footer</td>
                            <td>Footer section.</td>
                        </tr>
                        <tr>
                            <td>p-dataview-content</td>
                            <td>Container of items.</td>
                        </tr>
                            <tr>
                            <td>p-dataview-emptymessage</td>
                            <td>Empty message element.</td>
                        </tr>
                    </tbody>
                </table>
            </div>

            <h5>Dependencies</h5>
            <p>PrimeFlex.</p>
        </p-tabPanel>

        <p-tabPanel header="Source">
            <a href="https://github.com/primefaces/primeng/tree/master/src/app/showcase/components/dataview" class="btn-viewsource" target="_blank">
                <span>View on GitHub</span>
            </a>
            <a href="https://stackblitz.com/edit/primeng-dataview-demo" class="btn-viewsource" style="margin-left: .5em;" target="_blank">
                <span>Edit in StackBlitz</span>
            </a>

<app-code lang="markup" ngNonBindable ngPreserveWhitespaces>
&lt;div class="card"&gt;
    &lt;p-dataView #dv [value]="products" [paginator]="true" [rows]="9" filterBy="name"
        [sortField]="sortField" [sortOrder]="sortOrder" layout="grid"&gt;
        &lt;ng-template pTemplate="header"&gt;
            &lt;div class="flex flex-column md:flex-row md:justify-content-between"&gt;
                &lt;p-dropdown [options]="sortOptions" [(ngModel)]="sortKey" placeholder="Sort By Price" (onChange)="onSortChange($event)" styleClass="mb-2 md:mb-0"&gt;&lt;/p-dropdown&gt;
                &lt;span class="p-input-icon-left mb-2 md:mb-0"&gt;
                    &lt;i class="pi pi-search"&gt;&lt;/i&gt;
                    &lt;input type="search" pInputText placeholder="Search by Name" (input)="dv.filter($event.target.value)"&gt;
                &lt;/span&gt;
                &lt;p-dataViewLayoutOptions&gt;&lt;/p-dataViewLayoutOptions&gt;
            &lt;/div&gt;
        &lt;/ng-template&gt;
        &lt;ng-template let-product pTemplate="listItem"&gt;
            &lt;div class="col-12"&gt;
                &lt;div class="product-list-item"&gt;
                    &lt;img [src]="'assets/showcase/images/demo/product/' + product.image" [alt]="product.name"/&gt;
                    &lt;div class="product-list-detail"&gt;
                        &lt;div class="product-name"&gt;&#123;&#123;product.name&#125;&#125;&lt;/div&gt;
                        &lt;div class="product-description"&gt;&#123;&#123;product.description&#125;&#125;&lt;/div&gt;
                        &lt;p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"&gt;&lt;/p-rating&gt;
                        &lt;i class="pi pi-tag product-category-icon"&gt;&lt;/i&gt;&lt;span class="product-category"&gt;&#123;&#123;product.category&#125;&#125;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="product-list-action"&gt;
                        &lt;span class="product-price"&gt;$&#123;&#123;product.price&#125;&#125;&lt;/span&gt;
                        &lt;p-button icon="pi pi-shopping-cart" label="Add to Cart" [disabled]="product.inventoryStatus === 'OUTOFSTOCK'"&gt;&lt;/p-button&gt;
                        &lt;span [class]="'product-badge status-' + product.inventoryStatus.toLowerCase()"&gt;&#123;&#123;product.inventoryStatus&#125;&#125;&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/ng-template&gt;
        &lt;ng-template let-product pTemplate="gridItem"&gt;
            &lt;div class="col-12 md:col-4"&gt;
                &lt;div class="product-grid-item card"&gt;
                    &lt;div class="product-grid-item-top"&gt;
                        &lt;div&gt;
                            &lt;i class="pi pi-tag product-category-icon"&gt;&lt;/i&gt;
                            &lt;span class="product-category"&gt;&#123;&#123;product.category&#125;&#125;&lt;/span&gt;
                        &lt;/div&gt;
                        &lt;span [class]="'product-badge status-' + product.inventoryStatus.toLowerCase()"&gt;&#123;&#123;product.inventoryStatus&#125;&#125;&lt;/span&gt;
                    &lt;/div&gt;
                    &lt;div class="product-grid-item-content"&gt;
                        &lt;img [src]="'assets/showcase/images/demo/product/' + product.image" [alt]="product.name"/&gt;
                        &lt;div class="product-name"&gt;&#123;&#123;product.name&#125;&#125;&lt;/div&gt;
                        &lt;div class="product-description"&gt;&#123;&#123;product.description&#125;&#125;&lt;/div&gt;
                        &lt;p-rating [ngModel]="product.rating" [readonly]="true" [cancel]="false"&gt;&lt;/p-rating&gt;
                    &lt;/div&gt;
                    &lt;div class="product-grid-item-bottom"&gt;
                        &lt;span class="product-price"&gt;$&#123;&#123;product.price&#125;&#125;&lt;/span&gt;
                        &lt;p-button icon="pi pi-shopping-cart" [disabled]="product.inventoryStatus === 'OUTOFSTOCK'"&gt;&lt;/p-button&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/ng-template&gt;
    &lt;/p-dataView&gt;
&lt;/div&gt;
</app-code>

<app-code lang="typescript" ngNonBindable ngPreserveWhitespaces>
export class DataViewDemo implements OnInit &#123;

    products: Product[];

    sortOptions: SelectItem[];

    sortOrder: number;

    sortField: string;

    constructor(private productService: ProductService) &#123; &#125;

    ngOnInit() &#123;
        this.productService.getProducts().then(data => this.products = data);

        this.sortOptions = [
            &#123;label: 'Price High to Low', value: '!price'&#125;,
            &#123;label: 'Price Low to High', value: 'price'&#125;
        ];
    &#125;
    
    onSortChange(event) &#123;
        let value = event.value;

        if (value.indexOf('!') === 0) &#123;
            this.sortOrder = -1;
            this.sortField = value.substring(1, value.length);
        &#125;
        else &#123;
            this.sortOrder = 1;
            this.sortField = value;
        &#125;
    &#125;
&#125;
</app-code>


        </p-tabPanel>
        <p-tabPanel header="StackBlitz">
            <ng-template pTemplate="content">
                <iframe src="https://stackblitz.com/edit/primeng-dataview-demo?embed=1" style="width: 100%; height: 768px; border: none;"></iframe>
            </ng-template>
        </p-tabPanel>
    </p-tabView>
</div>
